﻿#define _USE_MATH_DEFINES
#include <math.h>
#include <assert.h>

#include "GetTriangles.h"

class SinCosTable
{
public:
	SinCosTable() {
		for (int i = 0; i <= 45; ++i)
		{
			double angle = double(i) * M_PI / 180;
			sinTable[i] = sin(angle);
			cosTable[i] = cos(angle);
		}
	}

	double sinValue(int n)
	{
		while (n < 0)
			n += 360;
		while (n >= 360)
			n -= 360;
		if (n <= 45)
		{
			return sinTable[n];
		}
		else if (n <= 90)
		{
			n = 90 - n;
			return cosTable[n];
		}
		else if (n <= 135)
		{
			n = n - 90;
			return cosTable[n];
		}
		else if (n <= 180)
		{
			n = 180 - n;
			return sinTable[n];
		}
		else
		{
			n -= 180;
			return -sinValue(n);
		}
	}

	double cosValue(int n)
	{
		while (n < 0)
			n += 360;
		while (n >= 360)
			n -= 360;
		if (n <= 45)
		{
			return cosTable[n];
		}
		else if (n <= 90)
		{
			n = 90 - n;
			return sinTable[n];
		}
		else if (n <= 135)
		{
			n = n - 90;
			return -sinTable[n];
		}
		else if (n <= 180)
		{
			n = 180 - n;
			return -cosTable[n];
		}
		else
		{
			n -= 180;
			return -cosValue(n);
		}
	}
private:
	double sinTable[45+1];
	double cosTable[45+1];
};

static SinCosTable sincosTable;


void appendPoints(osg::Vec3Array * va, const std::vector<osg::Vec3d>& points)
{
	assert(va);
	for (const auto& pt : points)
	{
		va->push_back(pt);
	}
}

// 计算sin(A + n * PI/180)
double sinValue(double sinA, double cosA, int n)
{
	return sinA * sincosTable.cosValue(n) + cosA * sincosTable.sinValue(n);
}

// 计算cos(A + n * PI/180)
double cosValue(double sinA, double cosA, int n)
{
	return cosA * sincosTable.cosValue(n) - sinA * sincosTable.sinValue(n);
}

void ArbitraryAxis(const osg::Vec3d & zAxis, osg::Vec3d & xAxis, osg::Vec3d & yAxis)
{
	const double standard = 1.0 / 64.0;
	if (std::abs(zAxis.x()) < standard && std::abs(zAxis.y()) < standard)
	{
		xAxis = osg::Vec3d(0, 1, 0) ^ zAxis;
	}
	else
	{
		xAxis = osg::Vec3d(0, 0, 1) ^ zAxis;
	}
	xAxis.normalize();
	yAxis = zAxis ^ xAxis;
	yAxis.normalize();
}

std::vector<osg::Vec3d> getCirclePoints(const osg::Vec3d & center, const osg::Vec3d & axis, double radius, int n)
{
	assert(n > 0 && (360 % n == 0));
	double sin0 = 0;
	double cos0 = 1;
	const int step = 360 / n;
	std::vector<osg::Vec3d> points;
	if (radius == 0.0)
	{
		return points;
	}
	osg::Vec3d xAxis, yAxis;
	ArbitraryAxis(axis, xAxis, yAxis);
	for (int i = 0; i < n; i++)
	{
		auto pt = center +
			xAxis * cosValue(sin0, cos0, step * i) * radius +
			yAxis * sinValue(sin0, cos0, step * i) * radius;
		points.push_back(pt);
	}
	return points;
}

std::vector<osg::Vec3d> getArcPoints(const double& startAngle, const double& endAngle,
	const double& radius, const osg::Vec3d& center, const osg::Vec3d & axis, int step/*degree*/)
{
	std::vector<osg::Vec3d> result;
	double sinA = sin(startAngle);
	double cosA = cos(startAngle);
	double detA = (endAngle - startAngle) * 180 / M_PI;
	int count = detA / step;
	if (detA <= 0)
		return result;

	osg::Vec3d xAxis, yAxis;
	ArbitraryAxis(axis, xAxis, yAxis);
	for (int i = 0; i <= count; i++)
	{
		double sinAi = sinValue(sinA, cosA, i * step);
		double cosAi = cosValue(sinA, cosA, i * step);

		auto pt = center +
			xAxis * cosAi * radius +
			yAxis * sinAi * radius;

		result.push_back(pt);
	}

	if ((detA - step * count) > 0.1/*degree*/)
	{
		double sinA = sin(endAngle);
		double cosA = cos(endAngle);

		auto pt = center +
			xAxis * cosA * radius +
			yAxis * sinA * radius;

		result.push_back(pt);
	}
	return result;
}

std::vector<osg::Vec3d> getDiskTriangles(const osg::Vec3d& center, const std::vector<osg::Vec3d>& circlePoints, bool reverse)
{
	std::vector<osg::Vec3d> triangles;
	int circlePointsCount = circlePoints.size();
	assert(circlePointsCount >= 3);
	for (int i = 0; i < circlePointsCount; i++)
	{
		int ip1 = (i + 1) % circlePointsCount;
		triangles.push_back(center);
		if (!reverse)
		{
			triangles.push_back(circlePoints[i]);
			triangles.push_back(circlePoints[ip1]);
		}
		else
		{
			triangles.push_back(circlePoints[ip1]);
			triangles.push_back(circlePoints[i]);
		}
	}
	return triangles;
}

std::vector<osg::Vec3d> getCirqueTriangles(const std::vector<osg::Vec3d>& outer, const std::vector<osg::Vec3d>& inner, bool reverse)
{
	std::vector<osg::Vec3d> triangles;
	int circlePointsCount = outer.size();
	assert(circlePointsCount >= 3);
	assert(circlePointsCount == inner.size());
	for (int i = 0; i < circlePointsCount; i++)
	{
		int ip1 = (i + 1) % circlePointsCount;
		//第一个三角形
		triangles.push_back(inner[i]);
		if (!reverse)
		{
			triangles.push_back(outer[i]);
			triangles.push_back(outer[ip1]);
		}
		else
		{
			triangles.push_back(outer[ip1]);
			triangles.push_back(outer[i]);
		}
		//第二个三角形
		triangles.push_back(inner[i]);
		if (!reverse)
		{
			triangles.push_back(outer[ip1]);
			triangles.push_back(inner[ip1]);
		}
		else
		{
			triangles.push_back(inner[ip1]);
			triangles.push_back(outer[ip1]);
		}
	}
	return triangles;
}

std::vector<osg::Vec3d> getCylinderSideTriangles(const std::vector<osg::Vec3d>& bottomPoints, const std::vector<osg::Vec3d>& topPoints)
{
	std::vector<osg::Vec3d> triangles;
	int bottomCirclePointCount = bottomPoints.size();
	for (int i = 0; i < bottomCirclePointCount; i++)
	{
		int ip1 = (i + 1) % bottomCirclePointCount;
		//第一个三角形
		triangles.push_back(bottomPoints[i]);
		triangles.push_back(bottomPoints[ip1]);
		triangles.push_back(topPoints[i]);
		//第二个三角形
		triangles.push_back(topPoints[i]);
		triangles.push_back(bottomPoints[ip1]);
		triangles.push_back(topPoints[ip1]);
	}
	return triangles;
}

std::vector<osg::Vec3d> getUpsidedownConeSideTriangles(const std::vector<osg::Vec3d>& topPoints, const osg::Vec3d & bottomCenter)
{
	std::vector<osg::Vec3d> triangles;
	for (int i = 0; i < topPoints.size(); i++)
	{
		int ip1 = (i + 1) % topPoints.size();
		triangles.push_back(bottomCenter);
		triangles.push_back(topPoints[ip1]);
		triangles.push_back(topPoints[i]);
	}
	return triangles;
}
